#include "ani_helpers_build_scripts.h"

#include <ani.h>
#include <array>
#include <chrono>
#include <iostream>
#include <thread>

static void checkExtends(ani_env *env) {

  ani_class clsB = nullptr;
  CHECK_ANI_FATAL(env->FindClass("main.B", &clsB));

  ani_method ctorB = nullptr;
  CHECK_ANI_FATAL(env->Class_FindMethod(clsB, "<ctor>", ":", &ctorB));

  ani_object objB = nullptr;
  CHECK_ANI_FATAL(env->Object_New(clsB, ctorB, &objB));

  ani_method mFoo = nullptr;
  CHECK_ANI_FATAL(env->Class_FindMethod(clsB, "foo", ":", &mFoo));

  CHECK_ANI_FATAL(env->Object_CallMethod_Void(objB, mFoo));
}

static ani_int Sum([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object object, ani_int a, ani_int b) {
  // std::this_thread::sleep_for(std::chrono::milliseconds(5000));

  ani_class errorClass = nullptr;

  CHECK_ANI_FATAL(env->FindClass("escompat.Error", &errorClass));

  ani_method errorCtor = nullptr;
  CHECK_ANI_FATAL(
      env->Class_FindMethod(errorClass, "<ctor>", "C{std.core.String}C{escompat.ErrorOptions}:", &errorCtor));

  ani_ref undefined = nullptr;
  CHECK_ANI_FATAL(env->GetUndefined(&undefined));

  ani_object errorObj = nullptr;
  CHECK_ANI_FATAL(env->Object_New(errorClass, errorCtor, &errorObj, undefined, undefined));

  // CHECK_ANI_FATAL(env->ThrowError(static_cast<ani_error>(errorObj)));

  checkExtends(env);

  constexpr unsigned long long lim = 1000000007;
  unsigned long long val = 1;
  // for (unsigned long long i = 0; i < 3000000000; ++i){
  // val *= 5;
  // if (val > lim) {
  // val %= lim;
  //}
  //}

  return val + b;
}

ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result) {
  ani_env *env;
  CHECK_ANI_FATAL(vm->GetEnv(ANI_VERSION_1, &env));

  ani_class cls;
  CHECK_ANI_FATAL(env->FindClass("main.Calc", &cls));

  std::array methods = {
      ani_native_function{"sum", "ii:i", reinterpret_cast<void *>(Sum)},
  };

  CHECK_ANI_FATAL(env->Class_BindNativeMethods(cls, methods.data(), methods.size()));

  *result = ANI_VERSION_1;
  return ANI_OK;
}
